using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace CodeLibrary.XML.Utils
{
    public class NodeInfo
    {
        public readonly string Name;
        public Hashtable Attributes { get; set; }
        public NodeInfo(string name)
        {
            Name = name;
            Attributes = new Hashtable();
        }
    }

    public class DocumentChangeEventArgs : EventArgs
    {

    }

    public class XMLHandler
    {
        
        public event EventHandler<DocumentChangeEventArgs> OnDocumentChanged;  

        public XMLHandler(string filename)
        {
            _filename = filename;
            InitDocument();
        }

        private void InitDocument()
        {
            if (!File.Exists(_filename))
            {
                throw new FileNotFoundException("");
            }
            _document = new XmlDocument();
            _document.Load(_filename);
            if (OnDocumentChanged != null)
            {
                OnDocumentChanged(this, new DocumentChangeEventArgs());
            }
            _datachanged = true;
        }
        private string GetRootNodeName()
        {
            return "0";
        }

        private string GetConfigPath(string nodeName)
        {
            return "/" + GetRootNodeName() + "/" + nodeName;
        }

        public XmlNode GetConfigNode(string relativeXPath)
        {
            return _document.SelectSingleNode(GetConfigPath(relativeXPath));
        }

        public void AddConfig(string relativeXPath, NodeInfo newChild)
        {
            XmlNode childNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
            if (childNode == null)
            {
                string parent = relativeXPath.Substring(0, relativeXPath.LastIndexOf("/", StringComparison.Ordinal));
                XmlNode oRootNode = _document.SelectSingleNode(GetConfigPath(parent));
                if (oRootNode != null)
                {
                    childNode = _document.CreateNode(XmlNodeType.Element, newChild.Name, null);
                    foreach (DictionaryEntry config in newChild.Attributes)
                    {
                        XmlAttribute oAttribute = _document.CreateAttribute((string)config.Key);
                        oAttribute.InnerText = config.Value != null ? config.Value.ToString() : "";
                        if (childNode.Attributes != null) 
                            childNode.Attributes.Append(oAttribute);
                    }
                    oRootNode.AppendChild(childNode);
                }
            }
        }

        public void RemoveConfig(string relativeXPath)
        {
            XmlNode oChildNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
            if ((oChildNode != null) && (oChildNode.ParentNode != null))
                oChildNode.ParentNode.RemoveChild(oChildNode);
        }

        public NodeInfo GetConfig(string relativeXPath)
        {
            XmlNode oChildNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
            if (oChildNode != null)
            {
                NodeInfo oInfo = new NodeInfo(oChildNode.Name);
                if (oChildNode.Attributes != null)
                {
                    foreach (XmlAttribute attr in oChildNode.Attributes)
                    {
                        oInfo.Attributes.Add(attr.Name, attr.InnerText);
                    }
                }
                return oInfo;
            }
            return null;
        }

        public IList<NodeInfo> GetSubConfigs(string relativeXPath)
        {
            XmlNode oNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
            List<NodeInfo> list = new List<NodeInfo>();
            if (oNode != null)
            {
                foreach (XmlNode node in oNode.ChildNodes)
                {
                    NodeInfo oInfo = new NodeInfo(node.Name);
                    if (node.Attributes != null)
                    {
                        foreach (XmlAttribute attr in node.Attributes)
                        {
                            oInfo.Attributes.Add(attr.Name, attr.InnerText);
                        }
                    }
                    list.Add(oInfo);
                }
            }
            return list;
        }

        public string GetConfigValue(string relativeXPath, string property)
        {
            if (_document != null)
            {
                XmlNode oRootNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
                if ((oRootNode != null) && (oRootNode.Attributes != null) && (oRootNode.Attributes[property] != null))
                    return oRootNode.Attributes[property].InnerText;
            }
            return "";
        }

        public void SetConfigValue(string relativeXPath, string property, string value)
        {
            XmlNode oRootNode = _document.SelectSingleNode(GetConfigPath(relativeXPath));
            if (oRootNode != null && oRootNode.Attributes != null && oRootNode.Attributes[property] != null)
            {
                oRootNode.Attributes[property].InnerText = value;
                _datachanged = true;
            }
        }

        public bool DataChanged
        {
            get { return _datachanged; }
        }

        private readonly string _filename;
        private XmlDocument _document;
        private bool _datachanged;
    }
}